package org.phalanx.gwt.client.screens;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.phalanx.gwt.client.graphics.PhalanxWebMapView;
import org.phalanx.gwt.client.graphics.map.MapCoordinates;
import org.phalanx.gwt.client.graphics.map.PhalanxMap;
import org.phalanx.gwt.client.graphics.map.PhalanxMapReader;
import org.phalanx.gwt.client.shared.MessageListener;
import org.phalanx.gwt.client.shared.MessageManager;
import org.phalanx.gwt.client.shared.PhalanxTeamPickerListBox;
import org.phalanx.gwt.client.shared.ServerMessage;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class JoinGameScreen extends VerticalPanel implements MessageListener, ChangeHandler, ClickHandler {
	// This class is the "room" where users get to
	// select different options before actually joining the game

	private static int USER_NAME = 0;
	private static int GAME_ALIAS = 1;
	private static int TEAM = 2;
	private static int BUILD_POINT = 3;

	private HashMap<String,ArrayList<String>> statusMatrix;

	private TextBox gameNameAlias;
	private PhalanxTeamPickerListBox teamPicker;
	private ListBox buildPoint;
	private PhalanxWebMapView mapView;

	private FlexTable membersTable;
	private MessageManager msgMgr ;
	private ContextPanelChooser chooser;

	private Integer gameId;
	private String gameName;
	private String mapName;

	private boolean loadingFeed;
	private PhalanxMap pMap;

	private HorizontalPanel mapPanel;

	private Button okButton;

	public JoinGameScreen(MessageManager msgMgr, ContextPanelChooser chooser){
		this.msgMgr = msgMgr;
		this.chooser = chooser;
	}

	public void setMap(PhalanxMap pMap){
		this.pMap = pMap;
	}
	public void initializeJoin(Integer gameId){
		this.setGameId(gameId);

		createComponents();
		createPanel();

		loadingFeed = true;

		ServerMessage joinGame = new ServerMessage();
		joinGame.setContext("game");
		joinGame.addMessageContents("action", "joinpregame");

		joinGame.addMessageContents("gameid", gameId.toString());

		msgMgr.addMessageListener("joinfeed", this);
		msgMgr.sendMessage(joinGame);
	}

	private void createComponents(){
		gameNameAlias = new TextBox();
		gameNameAlias.setWidth("10em");

		teamPicker = new PhalanxTeamPickerListBox();

		buildPoint = new ListBox();
		buildPoint.addItem("Select Build Point", "null");

		buildPoint.setEnabled(false);

		okButton = new Button("Confirm");

		createFlexTable();
	}

	private void createPanel(){
		mapPanel = new HorizontalPanel();

		this.add(mapPanel);

		FlexTable settingsTable = new FlexTable();

		addRowToSettings("Game settings: ", null, settingsTable);
		addRowToSettings("Game Alias: ", gameNameAlias, settingsTable);
		addRowToSettings("Team: ", teamPicker, settingsTable);
		addRowToSettings("Build Point: ", buildPoint, settingsTable);
		addRowToSettings(null, okButton, settingsTable);

		this.add(settingsTable);
		this.add(membersTable);
	}

	private void addRowToSettings(String labelText, Widget w, FlexTable table){
		Integer rowNumber = table.getRowCount();

		table.getFlexCellFormatter().setHorizontalAlignment(rowNumber, 0, HasHorizontalAlignment.ALIGN_LEFT);
		table.getFlexCellFormatter().setHorizontalAlignment(rowNumber, 1, HasHorizontalAlignment.ALIGN_LEFT);

		if (labelText == null && w != null){
			table.getFlexCellFormatter().setColSpan(rowNumber, 0, 2);
			table.setWidget(rowNumber, 0, w);
		} else if (w == null){
			table.getFlexCellFormatter().setColSpan(rowNumber, 0, 2);
			table.setWidget(rowNumber, 0, new Label(labelText));
		} else {
			table.setWidget(rowNumber, 0, new Label(labelText));
			table.setWidget(rowNumber, 1, w);
		}
	}

	public void setGameId(Integer gameId) {
		this.gameId = gameId;
	}

	public Integer getGameId() {
		return gameId;
	}

	public void setCachedMap(PhalanxMap pMap){
		if (pMap != null){
			this.mapName = pMap.getMapName();
			this.pMap = pMap;
		}
	}

	@Override
	public void onMessageReceived(ServerMessage msg) {
		if (msg.getContext().equals("joinfeed")){
			String serializedPlayerData = msg.getMessageContentsValue("players"); 

			if (loadingFeed){
				this.gameName = msg.getMessageContentsValue("gamename");

				if (pMap == null){
					ServerMessage getMapMsg = new ServerMessage();
					getMapMsg.setContext("loadmap");
					getMapMsg.addMessageContents("mapname", mapName);
					getMapMsg.setGET();

					msgMgr.sendMessage(getMapMsg);
					msgMgr.addMessageListener("mapdownload", this);
				} else {
					onMapLoad();
				}

				if (serializedPlayerData != null){
					modifyUserTableListing(serializedPlayerData);
					renderFlexTable();
				}

				loadingFeed = false;
			} else {
				if (serializedPlayerData != null){
					modifyUserTableListing(msg.getMessageContentsValue("players"));
					renderFlexTable();
				}
			}
		}
		else if (msg.getContext().equals("mapdownload")){
			msgMgr.removeMessageListener("mapdownload", this);
			loadUpMap(msg);
			onMapLoad();
		}
	}

	private void loadUpMap(ServerMessage mapMsg){
		PhalanxMapReader mapReader = new PhalanxMapReader();
		mapReader.setMapData(mapMsg);
		pMap = mapReader.getMap();
	}

	private void onMapLoad(){
		mapView = new PhalanxWebMapView(pMap, 220, 500);
		mapView.setZoomLevel(PhalanxWebMapView.ZOOMOUT_LEVEL_1);
		mapView.renderMap();
		mapPanel.add(mapView);
		mapPanel.setVisible(true);

		if (pMap.getBuildPoints().size() > 1){

			buildPoint.clear();

			buildPoint.addItem("Select Build Point", "null");

			Iterator<MapCoordinates> i = pMap.getBuildPoints().iterator();

			while (i.hasNext()){
				MapCoordinates c = i.next();

				String bpFaceValue = "(" + c.getX() + "," + c.getY() + ")";

				buildPoint.addItem(bpFaceValue);
			}

			if (buildPoint.getItemCount() > 0){
				buildPoint.setEnabled(true);
				buildPoint.addChangeHandler(this);
			}

			teamPicker.addChangeHandler(this);
			okButton.addClickHandler(this);
		}
	}

	private void removeUserFromTableListing(String userName){
		statusMatrix.remove(userName);
	}

	private void modifyUserTableListing(String serializedData){
		// Data is {gamealias!username!id!team!bpX!bpY}:{..!..}:...

		String[] joinStateArray = serializedData.split(":");

		if (joinStateArray.length > 0){
			for (int i = 0; i < joinStateArray.length; i++){
				String oneBundle = joinStateArray[i];

				oneBundle = oneBundle.substring(1,oneBundle.length() - 1);

				String[] bundleObjects = oneBundle.split("!");

				if (bundleObjects.length == 6){
					String gameAlias = bundleObjects[0];
					String userName = bundleObjects[1];
					String userId = bundleObjects[2];
					String team = bundleObjects[3];
					String buildPointX = bundleObjects[4];
					String buildPointY = bundleObjects[5];

					ArrayList<String> labelArray = new ArrayList<String>();
					labelArray.add(gameAlias);
					labelArray.add(userName);
					labelArray.add(userId);
					labelArray.add(team);
					labelArray.add(buildPointX);
					labelArray.add(buildPointY);

					statusMatrix.put(userName, labelArray);
				}
			}
		}
	}

	private void createFlexTable(){
		membersTable = new FlexTable();
		membersTable.setWidth("500px");
		membersTable.setCellPadding(3);
		membersTable.setCellSpacing(5);

		membersTable.setWidget(0, 0, new Label("Player"));
		membersTable.setWidget(0, 1, new Label("team"));
		membersTable.setWidget(0, 2, new Label("build point"));
	}

	private void renderFlexTable(){
		Iterator<ArrayList<String>> i = statusMatrix.values().iterator();

		while (i.hasNext()){
			ArrayList<String> tempArray = i.next();

			String userName = tempArray.get(1);

			Integer userRow = findUserNameFromTable(userName);

			if (userRow != null){
				// Modify row
				modifyRow(userRow, tempArray);
			} else {
				// Create a new row
				addRow(tempArray);
			}
		}
	}

	private Integer findUserNameFromTable(String user){
		Integer row = null;

		for (int i = 0; i < membersTable.getRowCount(); i++){
			Label userLabel = (Label)membersTable.getWidget(i, 0);
			String usernameMix = userLabel.getText();

			String[] aliasUserName = usernameMix.split(" ");

			if (aliasUserName.length > 1){
				String userName = aliasUserName[1];

				if (userName.equals(user)){
					row = i;
					break;
				}
			}
		}

		return row;
	}

	private void modifyRow(Integer row, ArrayList<String> statusUser){
		String userNameAliasDisplay = statusUser.get(0) + " (" + statusUser.get(1) + ")";

		membersTable.setWidget(row, 0, new Label(userNameAliasDisplay)); // userName
		membersTable.setWidget(row, 1, new Label(statusUser.get(3)));

		String buildPointDisplay = "(" + statusUser.get(4) + "," + statusUser.get(5) + ")";

		membersTable.setWidget(row, 2, new Label(buildPointDisplay));
	}

	private void addRow(ArrayList<String> statusUser){
		modifyRow(membersTable.getRowCount(), statusUser);
	}

	private void removeRow(Integer row){
		membersTable.removeRow(row);
	}

	@Override
	public void onChange(ChangeEvent event) {
		if (event.getSource().equals(buildPoint)){
			handleBuildPointSelection(true);
		}
	}

	@Override
	public void onClick(ClickEvent event) {
		// For confirm button

		if (event.getSource().equals(okButton)){
			ServerMessage msg = new ServerMessage();
			MapCoordinates c = handleBuildPointSelection(false);

			// TODO: Add validator

			msg.setContext("game");
			msg.addMessageContents("gameid", gameId.toString());
			msg.addMessageContents("action", "confirmsettings");
			msg.addMessageContents("team", teamPicker.getValue(teamPicker.getSelectedIndex()));
			msg.addMessageContents("buildpointx", c.getY().toString());
			msg.addMessageContents("buildpointy", c.getY().toString());

			msgMgr.sendMessage(msg);
		}
	}

	private MapCoordinates handleBuildPointSelection(boolean renderToMap){
		String bpSelection = buildPoint.getValue(buildPoint.getSelectedIndex());
		MapCoordinates c = null;

		if (bpSelection != null && !bpSelection.isEmpty()){
			bpSelection = bpSelection.substring(1, bpSelection.length() - 1);

			String[] xyCoor = bpSelection.split(",");

			if (xyCoor.length > 1){
				Integer coor1 = Integer.valueOf(xyCoor[0]);
				Integer coor2 = Integer.valueOf(xyCoor[1]);

				c = new MapCoordinates(coor1, coor2);

				if (renderToMap){
					mapView.setCoordinates(coor1, coor2);
					mapView.renderMap();
				}
			}
		}

		return c;
	}
}
